// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package stream_test

import (
	"testing"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/stream"

	. "github.com/google/gapid/core/stream/fmts"
)

var (
	R = stream.Channel_Red
	G = stream.Channel_Green
	B = stream.Channel_Blue
	A = stream.Channel_Alpha
)

func swizzle(f *stream.Format, s ...stream.Channel) *stream.Format {
	out, err := f.Swizzle(s...)
	if err != nil {
		panic(err)
	}
	return out
}

func TestSwizzle(t *testing.T) {
	assert := assert.To(t)
	data := []byte{
		0x0, 0x1, 0x2, 0x3,
		0x4, 0x5, 0x6, 0x7,
		0x8, 0x9, 0xa, 0xb,
	}
	for _, test := range []struct {
		dst, src *stream.Format
		expected []byte
	}{
		{swizzle(RGB_U8, G, R, B), RGB_U8, []byte{
			0x1, 0x0, 0x2,
			0x4, 0x3, 0x5,
			0x7, 0x6, 0x8,
			0xa, 0x9, 0xb,
		}},
		{swizzle(RGB_F32, G, R, B), RGB_F32, []byte{
			0x4, 0x5, 0x6, 0x7,
			0x0, 0x1, 0x2, 0x3,
			0x8, 0x9, 0xa, 0xb,
		}},
		{swizzle(RGB_F32, G, G, G), RGB_F32, []byte{
			0x4, 0x5, 0x6, 0x7,
			0x4, 0x5, 0x6, 0x7,
			0x4, 0x5, 0x6, 0x7,
		}},
	} {
		out, err := stream.Convert(test.dst, test.src, data)
		if assert.For("dst %v src %v", test.dst, test.src).ThatError(err).Succeeded() {
			assert.For("dst %v src %v", test.dst, test.src).ThatSlice(out).Equals(test.expected)
		}
	}
}

type convertTest struct{ dst, src *stream.Format }
type convertTests []convertTest

func (tests convertTests) run(t *testing.T, patterns map[*stream.Format][]byte) {
	assert := assert.To(t)
	for _, test := range tests {
		out, err := stream.Convert(test.dst, test.src, patterns[test.src])
		if assert.For("dst %v src %v", test.dst, test.src).ThatError(err).Succeeded() {
			assert.For("dst %v src %v", test.dst, test.src).ThatSlice(out).Equals(patterns[test.dst])
		}
	}
}
func TestConvertUint1(t *testing.T) {
	convertTests{
		{RGB_U8, RGB_U1}, {RGB_U1, RGB_U8},
		{RGB_U8, RGB_U4}, {RGB_U4, RGB_U8},
	}.run(t, map[*stream.Format][]byte{
		RGB_U1: {
			0x01, // red
			0x02, // green
			0x04, // blue
			0x07, // white
			0x00, // black
		},
		RGB_U4: {
			0x01, 0x00, // red
			0x10, 0x00, // green
			0x00, 0x01, // blue
			0x11, 0x01, // white
			0x00, 0x00, // black
		},
		RGB_U8: {
			0x01, 0x00, 0x00, // red
			0x00, 0x01, 0x00, // green
			0x00, 0x00, 0x01, // blue
			0x01, 0x01, 0x01, // white
			0x00, 0x00, 0x00, // black
		},
	})
}

func TestConvertSint(t *testing.T) {
	convertTests{
		{XYZ_S8, XYZ_S16}, {XYZ_S16, XYZ_S8},
	}.run(t, map[*stream.Format][]byte{
		XYZ_S8: {
			0x01, 0x01, 0x01,
			0xff, 0xff, 0xff,
		},
		XYZ_S16: {
			0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
			0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		},
	})
}

func TestConvertUintNorm(t *testing.T) {
	convertTests{
		{RGB_U8_NORM, RGB_U1_NORM}, {RGB_U1_NORM, RGB_U8_NORM},
		{RGB_U8_NORM, RGB_U4_NORM}, {RGB_U4_NORM, RGB_U8_NORM},
		{RGB_U8_NORM, RGB_U5_NORM}, {RGB_U5_NORM, RGB_U8_NORM},
	}.run(t, map[*stream.Format][]byte{
		RGB_U1_NORM: {
			0x01, // red
			0x02, // green
			0x04, // blue
			0x07, // white
			0x00, // black
		},
		RGB_U4_NORM: {
			0x0f, 0x00, // red
			0xf0, 0x00, // green
			0x00, 0x0f, // blue
			0xff, 0x0f, // white
			0x00, 0x00, // black
		},
		RGB_U5_NORM: {
			0x1f, 0x00, // red
			0xe0, 0x03, // green
			0x00, 0x7c, // blue
			0xff, 0x7f, // white
			0x00, 0x00, // black
		},
		RGB_U8_NORM: {
			0xff, 0x00, 0x00, // red
			0x00, 0xff, 0x00, // green
			0x00, 0x00, 0xff, // blue
			0xff, 0xff, 0xff, // white
			0x00, 0x00, 0x00, // black
		},
	})
}

func TestConvertSintNorm(t *testing.T) {
	convertTests{
		{XYZ_S8_NORM, XYZ_S16_NORM}, {XYZ_S16_NORM, XYZ_S8_NORM},
	}.run(t, map[*stream.Format][]byte{
		XYZ_S8_NORM: {
			0x00, 0x00, 0x00,
			0x00, 0x7f, 0xff,
		},
		XYZ_S16_NORM: {
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0xff, 0x7f, 0xff, 0xff,
		},
	})
}

func TestConvertS8NormF32(t *testing.T) {
	convertTests{
		{XYZ_S8_NORM, XYZ_F32}, {XYZ_F32, XYZ_S8_NORM},
	}.run(t, map[*stream.Format][]byte{
		XYZ_S8_NORM: {
			0x00, 0x01, 0xff, // 0,   1,   -1
			0x00, 0x7f, 0x80, // 0,  127, -128
		},
		XYZ_F32: {
			0x81, 0x80, 0x80, 0x3b, // 2 * (128 / 255 - 0.5) =  0.003921568
			0xc1, 0xc0, 0x40, 0x3c, // 2 * (129 / 255 - 0.5) =  0.011764706
			0x81, 0x80, 0x80, 0xbb, // 2 * (127 / 255 - 0.5) = -0.003921568

			0x81, 0x80, 0x80, 0x3b, // 2 * (128 / 255 - 0.5) =  0.003921568
			0x00, 0x00, 0x80, 0x3f, // 2 * (255 / 255 - 0.5) =  1
			0x00, 0x00, 0x80, 0xbf, // 2 * (0   / 255 - 0.5) = -1
		},
	})
}

func TestConvertS16NormF32(t *testing.T) {
	convertTests{
		{XYZ_F32, XYZ_S16_NORM}, {XYZ_S16_NORM, XYZ_F32},
	}.run(t, map[*stream.Format][]byte{
		XYZ_S16_NORM: {
			0x00, 0x00, 0x01, 0x00, 0xff, 0xff, // 0,   1,   -1
			0x00, 0x00, 0xff, 0x7f, 0x00, 0x80, // 0,  32767, -32768
		},
		XYZ_F32: {
			0x80, 0x00, 0x80, 0x37, // 2 * (32768 / 65535 - 0.5) =  0.000015259
			0xc0, 0x00, 0x40, 0x38, // 2 * (32769 / 65535 - 0.5) =  0.000045777
			0x80, 0x00, 0x80, 0xb7, // 2 * (32767 / 65535 - 0.5) = -0.000015259

			0x80, 0x00, 0x80, 0x37, // 2 * (32768 / 65535 - 0.5) =  0.000015259
			0x00, 0x00, 0x80, 0x3f, // 2 * (65535 / 65535 - 0.5) =  1
			0x00, 0x00, 0x80, 0xbf, // 2 * (0     / 65535 - 0.5) = -1
		},
	})
}

func TestConvertUintNormFloat(t *testing.T) {
	convertTests{
		{RGB_F16, RGB_U1_NORM}, {RGB_U1_NORM, RGB_F16},
		{RGB_F16, RGB_U4_NORM}, {RGB_U4_NORM, RGB_F16},
		{RGB_F16, RGB_U5_NORM}, {RGB_U5_NORM, RGB_F16},
		{RGB_F16, RGB_U8_NORM}, {RGB_U8_NORM, RGB_F16},

		{RGB_F32, RGB_U1_NORM}, {RGB_U1_NORM, RGB_F32},
		{RGB_F32, RGB_U4_NORM}, {RGB_U4_NORM, RGB_F32},
		{RGB_F32, RGB_U5_NORM}, {RGB_U5_NORM, RGB_F32},
		{RGB_F32, RGB_U8_NORM}, {RGB_U8_NORM, RGB_F32},
	}.run(t, map[*stream.Format][]byte{
		RGB_U1_NORM: {
			0x01, // red
			0x02, // green
			0x04, // blue
			0x07, // white
			0x00, // black
		},
		RGB_U4_NORM: {
			0x0f, 0x00, // red
			0xf0, 0x00, // green
			0x00, 0x0f, // blue
			0xff, 0x0f, // white
			0x00, 0x00, // black
		},
		RGB_U5_NORM: {
			0x1f, 0x00, // red
			0xe0, 0x03, // green
			0x00, 0x7c, // blue
			0xff, 0x7f, // white
			0x00, 0x00, // black
		},
		RGB_U8_NORM: {
			0xff, 0x00, 0x00, // red
			0x00, 0xff, 0x00, // green
			0x00, 0x00, 0xff, // blue
			0xff, 0xff, 0xff, // white
			0x00, 0x00, 0x00, // black
		},

		RGB_F16: {
			0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, // red
			0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, // green
			0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, // blue
			0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, // white
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // black
		},
		RGB_F32: {
			0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // red
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, // green
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, // blue
			0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, // white
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // black
		},
	})
}

func TestConvertAddImplicit(t *testing.T) {
	convertTests{
		{RGBA_U8, RGB_U4},
		{RGBA_U8_NORM, RGB_U4},
		{RGBA_F32, RGB_U4},
	}.run(t, map[*stream.Format][]byte{
		RGB_U4: {
			0x0f, 0x00, // red
			0xf0, 0x00, // green
			0x00, 0x0f, // blue
			0xff, 0x0f, // white
			0x00, 0x00, // black
		},
		RGBA_U8: {
			0x0f, 0x00, 0x00, 0xff,
			0x00, 0x0f, 0x00, 0xff,
			0x00, 0x00, 0x0f, 0xff,
			0x0f, 0x0f, 0x0f, 0xff,
			0x00, 0x00, 0x00, 0xff,
		},
		RGBA_U8_NORM: {
			0x0f, 0x00, 0x00, 0xff,
			0x00, 0x0f, 0x00, 0xff,
			0x00, 0x00, 0x0f, 0xff,
			0x0f, 0x0f, 0x0f, 0xff,
			0x00, 0x00, 0x00, 0xff,
		},
		RGBA_F32: {
			0x00, 0x00, 0x70, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x41, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x70, 0x41, 0x00, 0x00, 0x70, 0x41, 0x00, 0x00, 0x70, 0x41, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
		},
	})
}

func TestConvertAddImplicitConstants(t *testing.T) {
	convertTests{
		{RGBA_U8, RGB_U4_NORM},
		{RGBA_U8_NORM, RGB_U4_NORM},
		{RGBA_F32, RGB_U4_NORM},
	}.run(t, map[*stream.Format][]byte{
		RGB_U4_NORM: {
			0x0f, 0x00, // red
			0xf0, 0x00, // green
			0x00, 0x0f, // blue
			0xff, 0x0f, // white
			0x00, 0x00, // black
		},
		RGBA_U8: {
			0xff, 0x00, 0x00, 0xff,
			0x00, 0xff, 0x00, 0xff,
			0x00, 0x00, 0xff, 0xff,
			0xff, 0xff, 0xff, 0xff,
			0x00, 0x00, 0x00, 0xff,
		},
		RGBA_U8_NORM: {
			0xff, 0x00, 0x00, 0xff,
			0x00, 0xff, 0x00, 0xff,
			0x00, 0x00, 0xff, 0xff,
			0xff, 0xff, 0xff, 0xff,
			0x00, 0x00, 0x00, 0xff,
		},
		RGBA_F32: {
			0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f,
		},
	})
}

func TestConvertAddImplicitFromGreyLum(t *testing.T) {
	convertTests{
		{RGB_U8, L_U8_NORM},
		{RGBA_U8, L_U8_NORM},
		{R_F32, L_U8_NORM},
		{RGB_U8, Gray_U8_NORM},
		{RGBA_U8, Gray_U16_NORM},
	}.run(t, map[*stream.Format][]byte{
		L_U8_NORM: {
			0x00, 0x40, 0x80, 0xff,
		},
		Gray_U8_NORM: {
			0x00, 0x40, 0x80, 0xff,
		},
		Gray_U16_NORM: {
			0x00, 0x00, 0x00, 0x40, 0x00, 0x80, 0x00, 0xff,
		},
		RGB_U8: {
			0x00, 0x00, 0x00,
			0x40, 0x40, 0x40,
			0x80, 0x80, 0x80,
			0xff, 0xff, 0xff,
		},
		RGBA_U8: {
			0x00, 0x00, 0x00, 0xff,
			0x40, 0x40, 0x40, 0xff,
			0x80, 0x80, 0x80, 0xff,
			0xff, 0xff, 0xff, 0xff,
		},
		R_F32: {
			0x00, 0x00, 0x00, 0x00,
			0x81, 0x80, 0x80, 0x3e,
			0x81, 0x80, 0x00, 0x3f,
			0x00, 0x00, 0x80, 0x3f,
		},
	})
}

func TestSharedExp(t *testing.T) {
	convertTests{
		{RGB_F32, RGBE_U9U9U9U5},
	}.run(t, map[*stream.Format][]byte{
		RGBE_U9U9U9U5: {
			// R, G, B == 0
			0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0xf8,

			0x00, 0x00, 0x04, 0x00, // R = 0, G = 0, B = 1, E = 2^-24
			0x00, 0x00, 0x04, 0x70, // R = 0, G = 0, B = 1, E = 2^-10
			0x00, 0x00, 0x04, 0xc0, // R = 0, G = 0, B = 1, E = 2^0
			0x00, 0x00, 0x04, 0xf8, // R = 0, G = 0, B = 1, E = 2^7

			0x04, 0x06, 0x08, 0xf8, // R = 4, G = 3, B = 2, E = 2^7
		},
		RGB_F32: {
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x33, // B = 0.000000059604645
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3a, // B = 0.0009765625
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, // B = 1.0000
			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, // B = 128.0

			0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0xc0, 0x43, 0x00, 0x00, 0x80, 0x43, // B = 256, G = 384, R = 512
		},
	})
}

func TestSRGB(t *testing.T) {
	// These are split into two as rounding makes the numbers asymetrical.
	// TODO: Try and reduce the rounding errors.
	convertTests{
		{SRGB_U8_NORM, RGB_U8_NORM},
	}.run(t, map[*stream.Format][]byte{
		RGB_U8_NORM: []byte{
			0x00, 0x00, 0x00,
			0xff, 0xff, 0xff,
			0x10, 0x50, 0x90,
		},
		SRGB_U8_NORM: []byte{
			0x00, 0x00, 0x00,
			0xfe, 0xfe, 0xfe,
			0x46, 0x97, 0xC5,
		},
	})
	convertTests{
		{RGB_U8_NORM, SRGB_U8_NORM},
	}.run(t, map[*stream.Format][]byte{
		RGB_U8_NORM: []byte{
			0x00, 0x00, 0x00,
			0xff, 0xff, 0xff,
			0x0f, 0x4e, 0x8e,
		},
		SRGB_U8_NORM: []byte{
			0x00, 0x00, 0x00,
			0xff, 0xff, 0xff,
			0x46, 0x97, 0xC5,
		},
	})
}
